package com.oildatacollection.imitate;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 传感器模拟
 */
@Slf4j
public class SensorImitate {
    private int time;            // 发送消息的次数
    private byte devId;          // 设备ID
    private int wellID;          // 井口ID
    private int dataLen;         // 数据码长度
    private int trueDataNum;     // 正确数据的个数
    private Thread sensorThread; // 传感器发送线程
    List<ByteBuf> dataList;
    private String ip;
    private int port;

    public SensorImitate(byte devId, int wellID, String ip, int port) {
        this.devId = devId;
        this.wellID = wellID;
        this.dataLen = 0;
        this.dataList = new ArrayList<>();
        this.sensorThread = null;
        this.ip = ip;
        this.port = port;
        this.trueDataNum = 0;
    }

    private float makeRandomFloat(float start, float end) {
        return (float) Math.random() * (end - start) + start;
    }

    private String getKey(int i) {
        String res;
        switch (i) {
            case 0:
                res = "flow";
                break;
            case 1:
                res = "temp";
                break;
            case 2:
                res = "press";
                break;
            case 3:
                res = "water";
                break;
            case 4:
                res = "oil";
                break;
            case 5:
                res = "gas";
                break;
            default:
                res = "error-type";
                break;
        }
        return res;
    }

    // 生成超长数据
    public void makeErrorDataForToLang(int time) {
        // 生成长度9999的数据
        for (int k = 0; k < time; k++) {
            // 写入事务处理标识符 2byte
            ByteBuf curBuf = ByteBufAllocator.DEFAULT.buffer();
            curBuf.writeShort(1);
            // 写入协议标识符    2byte
            curBuf.writeShort(0);
            // 写入长度   2byte
            short len = 9999;
            curBuf.writeShort(len);
            curBuf.writeBytes(new byte[]{'a', 'b', 'c', 'd'});
            log.trace("错误数据生成!");
            dataList.add(curBuf);
        }
    }

    // 生成普通数据
    public void makeGeneralData(int time) {
        // 2 + 2 + 2 + 1 + 1 + 24 = 32
        for (int k = 0; k < time; k++) {
            ByteBuf curBuf = ByteBufAllocator.DEFAULT.buffer();
            // 写入事务处理标识符 2byte
            curBuf.writeShort(1);
            // 写入协议标识符    2byte
            curBuf.writeShort(0);
            // 写入长度   2byte
            short len = 28 + 2 + 5;
            curBuf.writeShort(len);
            // 写入单元标识符    1byte
            curBuf.writeByte(devId);
            // 写入功能码       1byte
            curBuf.writeByte((byte) (0x10));
            // 写入寄存器地址
            curBuf.writeShort(0);
            // 写入寄存器数量
            curBuf.writeShort(12);
            // 写入字节计数
            curBuf.writeByte(28);
            /**
             * 数据码共28字节
             * 井口ID 4字节
             * 流量4字节 温度4字节 气压4字节
             * 含水率4字节 含油量4字节 含气率4字节
             */
            String valStr = "";
            curBuf.writeInt(wellID);
            valStr += "wellID:" + wellID + "; ";
            for (int i = 0; i < 6; i++) {
                float curVal = makeRandomFloat((float) 10.0, (float) 60.0);
                curBuf.writeFloat(curVal);
                valStr += (getKey(i) + ":" + curVal + "; ");
            }
            log.trace(valStr);
            dataList.add(curBuf);
        }
        trueDataNum += time;
    }

    public class SensorThread implements Runnable {
        private Map<Byte, Integer> devMap;

        public SensorThread() {
            devMap = new HashMap<>();
        }

        private void printWellState(byte d1, byte d2) {
            byte sign = (byte) 0b01000000;
            byte cur = 0;
            String res = "";
            for (int i = 0; i < 2; i++) {
                cur = (byte) (d2 & (sign << i));
                res += (cur != 0b00000000 ? "开":"关") + " ";
            }
            sign = (byte) 0b00000001;
            for (int i = 0; i < 8; i++) {
                cur = (byte) (d1 & (sign << i));
                res += (cur != 0b00000000 ? "开":"关") + " ";
            }
            log.trace("井口状态: " + res);
        }

        @Override
        public void run() {
            try {
                Bootstrap clientBootstrap = new Bootstrap();
                clientBootstrap.group(new NioEventLoopGroup());
                clientBootstrap.channel(NioSocketChannel.class);
                clientBootstrap.handler(new ChannelInitializer<SocketChannel>() {
                    @Override // 在连接建立后被调用
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(20, 4, 2));
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                // 事务号
                                short txnId = buf.readShort();
                                // 协议标识符
                                short proId = buf.readShort();
                                // 字节长度
                                short len = buf.readShort();
                                // 单元标识符
                                byte devId = buf.readByte();
                                // 功能码
                                byte funId = buf.readByte();
//                                log.trace("len = " + len);
                                if (len == 9) {
                                    // 读取寄存器地址
                                    buf.readShort();
                                    // 读取寄存器数量
                                    buf.readShort();
                                    // 读取字节计数
                                    buf.readByte();
                                    // 读取数据
                                    byte[] datas = new byte[2];
                                    buf.readBytes(datas);
                                    printWellState(datas[0], datas[1]);
                                    return;
                                }

                                String res = "";
                                if (funId == 0x10) {
                                    res += "正确返回 -- ";
                                    if (devMap.get(devId) == null) {
                                        devMap.put(devId, 1);
                                    } else {
                                        int cur = devMap.get(devId);
                                        cur++;
                                        devMap.put(devId, cur);
                                        if (cur == trueDataNum) {
                                            log.trace("devId:" + Byte.toUnsignedInt(devId) + ": 所发送的格式正确消息已被全部成功接收");
                                        }
                                    };
                                } else {
                                    res += "错误返回 -- ";
                                }
                                res += "txnID:"+txnId + "; proId:" + proId + "; len:" + len + "; devId:" + devId + "; funId:" + funId + ";";
//                                log.trace(res);
                                buf.release();
                            }
                        });
                    }
                });
                ChannelFuture connFuture = clientBootstrap.connect(new InetSocketAddress(ip, port));
                Channel channel = connFuture.sync().channel();
                for (ByteBuf item : dataList) {
                    Thread.sleep(1000);
                    channel.writeAndFlush(item);
                }
                channel.closeFuture().sync();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 发送到服务器中
    public void sendToServer() throws InterruptedException {
        if (sensorThread != null) {
            sensorThread.join();
        }
        sensorThread = new Thread(new SensorThread());
        sensorThread.start();
    }

    // 等待发送结束
    public void waitForEnd() throws InterruptedException {
        if (sensorThread != null) {
            sensorThread.join();
        }
    }
}
